package hip.util.xml;

import hip.util.StackTraceUtil;
import hip.util.file.FileUtil;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.*;

import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;

/**
 * 对dom节点操作的工具类
 */
public class ElementUtil {

    /**
     * 初始化xml文件
     *
     * @throws IOException
     */
    public static void initXml(String path, String rootName, Map<String, Object> prop) throws IOException {
        if (path != null) {
            Document doc = DocumentHelper.createDocument();
            Element root = doc.addElement(rootName);
            if (prop != null) {
                Set<Entry<String, Object>> sets = prop.entrySet();
                for (Entry<String, Object> en : sets) {
                    root.addAttribute(en.getKey(), en.getValue().toString());
                }
            }
            String xmlStr = XmlUtil.formatXml(doc);
            FileUtil.writeTo(xmlStr, path);
        }
    }

    /**
     * 根据子节点获取节点注释
     */
    @SuppressWarnings("unchecked")
    public static String getComment(Element parent, String xmlPath) {
        if (parent != null && !parent.getUniquePath().equals(xmlPath)) {
            List<Node> nodes = parent.content();
            for (int i = 0; i < nodes.size(); i++) {
                Node node = nodes.get(i);
                String nodePath = node.getUniquePath();
                if (nodePath.equals(xmlPath)) {//顺序为：comment->text->element或comment->element，所以应向前推2或1，获取comment
                    int commentIndex = i - 1;
                    if (commentIndex >= 0) {
                        Node preNode = nodes.get(commentIndex);
                        String typeName = preNode.getNodeTypeName();
                        if ("comment".equalsIgnoreCase(typeName)) {
                            return preNode.getText();
                        } else if ("text".equalsIgnoreCase(typeName)) {
                            if (commentIndex - 1 >= 0) {
                                Node preNode2 = nodes.get(commentIndex - 1);
                                String typeName2 = preNode2.getNodeTypeName();
                                if ("comment".equalsIgnoreCase(typeName2)) {
                                    return preNode2.getText();
                                }
                            }
                        }
                    }
                }
            }
        }
        return "";
    }

    public static Document setComment(Document doc, String xmlpath, String commentNew) {
        if (doc != null) {
            Element root = doc.getRootElement();
            List<Element> es = getEles(root, xmlpath);
            if (!es.isEmpty()) {
                Element e = es.get(0);
                if (xmlpath != null) {
                    Element parent = e.getParent();
                    if (parent != null) {
                        String commentOld = getComment(parent, xmlpath);
                        String pXml = parent.asXML();
                        String[] sArr = pXml.split(commentOld);
                        if (sArr.length > 1) {
                            StringBuilder sb = new StringBuilder();
                            sb.append(sArr[0]);
                            for (int i = 1; i < sArr.length; i++) {
                                String ns = e.getNamespace().asXML();
                                String[] eArr = e.asXML().split(ns);
                                String indexStr;
                                if (eArr.length > 1) {
                                    indexStr = eArr[0].trim() + eArr[1];
                                } else {
                                    indexStr = e.asXML();
                                }
                                if (sArr[i].contains(indexStr)) {
                                    sb.append(commentNew).append(sArr[i]);
                                } else {
                                    sb.append(commentOld).append(sArr[i]);
                                }
                            }
                            return updateXml(doc, sb.toString(), parent.getUniquePath());
                        }
                    }
                }
            }
        }
        return doc;
    }

    /**
     * 根据xml路径更新Xml字符串，并返回doc
     */
    public static Document updateXml(Document doc, String xmlFile, String xmlpath) {
        if (xmlFile != null && doc != null && xmlpath != null) {
            if (isRootPath(xmlpath)) {
                try {
                    return DocumentHelper.parseText(xmlFile);
                } catch (DocumentException e) {
                    e.printStackTrace();
                    throw new RuntimeException("xml内容不规范");
                }
            }
            //需要先校验xmlfile是否规范
            List<Element> eles = ElementUtil.getEle(doc.getRootElement(), xmlpath);
            Element ele = null;
            if (eles != null && eles.size() > 0) {
                ele = eles.get(0);
                ele.addAttribute("_del", "true");

                try {
                    Document nDoc = DocumentHelper.parseText(xmlFile);
                    Element newEle = nDoc.getRootElement();
                    List<Element> newEles = new ArrayList<>();
                    newEles.add(newEle);
                    ElementUtil.replaceEleByProp(doc, "_del", newEles);
                    String newXml = XmlUtil.formatXml(doc);
                    return DocumentHelper.parseText(newXml);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException("xml内容不规范" + StackTraceUtil.getStackTrace(e));
                }
            }
            return doc;
        }
        return null;
    }

    /**
     * 根据属性更新xml
     *
     * @throws IOException
     */
    @SuppressWarnings("unchecked")
    public static void updateXmlByProp(String path, Map<String, Object> newData, Map<String, Object> oldData, String prop) throws IOException {
        if (oldData != null && newData != null && prop != null) {
            if (path != null) {
                Document doc = FileUtil.readDocFrom(path);
                if (doc != null) {
                    Element root = doc.getRootElement();
                    List<Element> elements = root.elements();
                    for (Element e : elements) {
                        String attrV = e.attributeValue(prop);
                        if (attrV.equals(oldData.get(prop))) {
                            Set<Entry<String, Object>> sets = newData.entrySet();
                            for (Entry<String, Object> en : sets) {
                                e.addAttribute(en.getKey(), en.getValue().toString());
                            }
                            break;
                        }
                    }
                    String xml = XmlUtil.formatXml(doc);
                    FileUtil.writeTo(xml, path);
                }
            }
        }
    }

    /**
     * 添加新节点到xml
     *
     * @throws IOException
     */
    public static void addtoXml(String path, Map<String, Object> newData) throws IOException {
        if (newData != null) {
            if (path != null) {
                Document doc = FileUtil.readDocFrom(path);
                if (doc != null) {
                    Element root = doc.getRootElement();
                    if (root.hasContent()) {
                        Element e = (Element) root.elements().get(0);
                        Element element = root.addElement(e.getName());
                        Set<Entry<String, Object>> sets = newData.entrySet();
                        for (Entry<String, Object> en : sets) {
                            element.addAttribute(en.getKey(), en.getValue().toString());
                        }
                        String xml = XmlUtil.formatXml(doc);
                        FileUtil.writeTo(xml, path);
                    }
                }
            }
        }
    }

    public static boolean isRootPath(String xmlPath) {
        if (xmlPath != null) {
            int pos = xmlPath.substring(1).indexOf("/");
            return pos == -1;
        }
        return false;
    }

    /**
     * 根据路径获取根节点下的子节点列表
     */
    @SuppressWarnings("unchecked")
    public static List<Element> getRootEles(String filepath) {
        if (StringUtils.isNotBlank(filepath)) {
            Document doc = FileUtil.readDocFrom(filepath);
            Element root = doc.getRootElement();
            return root.elements();
        }
        return null;
    }

    /**
     * 根据xpath获取节点
     */
    @SuppressWarnings("unchecked")
    public static List<Element> getEle(Element root, String xmlPath) {
        if (root != null && xmlPath != null) {
            List<Node> nodes = root.selectNodes(xmlPath);
            if (nodes != null && nodes.size() > 0) {
                List<Element> es = new ArrayList<>();
                for (Node node : nodes) {
                    if (node instanceof Element) {
                        Element e = (Element) node;
                        es.add(e);
                    }
                }
                return es;
            }
        }
        return null;
    }

    /**
     * 根据xpath获取节点
     * /*[name()='ClinicalDocument']/*[name()='section'][code [@code='46209-3']]/*[name()='observation'][code[@displayName='医嘱项目内容']]/*[name()='participant'][participantRole[code[@displayName='医嘱审核人']]]/*[name()='time']
     */
    public static List<Element> getEles(Element root, String xmlPath) {
        List<Element> results = new ArrayList<>();
        getEles(root, xmlPath, 0, results);
        return results;
    }

    @SuppressWarnings("unchecked")
    public static void getEles(Element root, String xmlPath, int count, List<Element> results) {
        if (root != null && xmlPath != null) {
            if (xmlPath.length() < count) {
                List<Element> list = getEle(root, xmlPath);
                if (list != null) {
                    results.addAll(list);
                }
            }
            int pos = xmlPath.indexOf("[@", count);
            if (pos != -1) {//有属性
                String left = xmlPath.substring(0, pos);
                String right = xmlPath.substring(pos);
                int posP = left.lastIndexOf("/");
                String nodeNameB = left.substring(posP + 1);
                int pre = nodeNameB.indexOf("name()=");
                if (pre == -1) {
                    pre = 0;
                }
                int posC = nodeNameB.substring(pre).indexOf("[");
                if (posC != -1) {//有子元素
                    String xpath = left.substring(0, posP + 1) + nodeNameB.substring(0, posC + pre);
                    List<Element> list = getEle(root, xpath);

                    String attrN = right.split("=")[0].substring(2);
                    String attrV = right.split("'")[1];

                    String cName = nodeNameB.substring(posC + pre + 1);
                    int cNamePos = cName.indexOf("[");
                    if (cNamePos != -1) {//还有下级子元素
                        String cName1 = cName.substring(0, cNamePos);
                        String cName2 = cName.substring(cNamePos + 1);
                        if (list != null && list.size() > 0) {
                            int countEle = 0;//计数相同节点递归次数
                            for (Element e : list) {
                                List<Element> childEles = e.elements();
                                if (childEles != null && childEles.size() > 0) {
                                    for (Element cEle : childEles) {
                                        if (cName1.equals(cEle.getName())) {
                                            List<Element> childEles2 = cEle.elements();
                                            for (Element cEle2 : childEles2) {
                                                if (cName2.equals(cEle2.getName())) {
                                                    List<Attribute> attrs = cEle2.attributes();
                                                    for (Attribute attr : attrs) {
                                                        if (attrN.equals(attr.getName()) && attrV.equals(attr.getValue())) {
                                                            e.addAttribute("_" + attrN, attrV);
                                                            int rPos = right.indexOf("/");
                                                            String newPath;
                                                            if (rPos != -1) {
                                                                newPath = xpath + "[@_" + attrN + "='" + attrV + "']" + right.substring(rPos);
                                                            } else {
                                                                newPath = xpath + "[@_" + attrN + "='" + attrV + "']";
                                                            }
                                                            count = xpath.length() + 2;
                                                            if (countEle > 0) {//多次递归取最后一次查询结果返回
                                                                results.clear();
                                                            }
                                                            getEles(root, newPath, count, results);//递归检查后半部分有无同样表达式
                                                            countEle++;
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        if (list != null && list.size() > 0) {
                            int countEle = 0;//计数相同节点递归次数
                            for (Element e : list) {
                                List<Element> childEles = e.elements();
                                if (childEles != null && childEles.size() > 0) {
                                    for (Element cEle : childEles) {
                                        if (cName.equals(cEle.getName())) {
                                            List<Attribute> attrs = cEle.attributes();
                                            for (Attribute attr : attrs) {
                                                if (attrN.equals(attr.getName()) && attrV.equals(attr.getValue())) {
                                                    e.addAttribute("_" + attrN, attrV);
                                                    int rPos = right.indexOf("/");
                                                    String newPath;
                                                    if (rPos != -1) {
                                                        newPath = xpath + "[@_" + attrN + "='" + attrV + "']" + right.substring(rPos);
                                                    } else {
                                                        newPath = xpath + "[@_" + attrN + "='" + attrV + "']";
                                                    }
                                                    count = xpath.length() + 2;
                                                    if (countEle > 0) {//多次递归取最后一次查询结果返回
                                                        results.clear();
                                                    }
                                                    getEles(root, newPath, count, results);//递归检查后半部分有无同样表达式
                                                    countEle++;
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                } else {//无子元素
                    int posA = right.indexOf("/");
                    if (posA != -1) {
                        count = pos + 2;
                        getEles(root, xmlPath, count, results);//递归检查后半部分有无同样表达式
                    } else {//最后一个节点
                        List<Element> list = getEle(root, xmlPath);
                        if (list != null) {
                            results.addAll(list);
                        }
                    }
                }
            } else {//无属性
                List<Element> list = getEle(root, xmlPath);
                if (list != null) {
                    results.addAll(list);
                }
            }
        }
    }

    /**
     * 根据xpath生成节点(支持下标)
     */
    @SuppressWarnings("unchecked")
    public static Element createEleByXpath(Element root, String xpath) {
        String[] txi = xpath.substring(1).substring(xpath.substring(1).indexOf("/") + 1).split("/");
        Element lsE = root;
        for (String si : txi) {
            if (StringUtils.isNotBlank(si)) {
                int xb = 1;
                String nb;
                String[] xbs;
                if (si.contains("=")) {//包含=情况，如：/*[name()='ClinicalDocument']/*[name()='typeId']
                    String sp = si.substring(si.indexOf("'") + 1, si.lastIndexOf("'"));
                    nb = sp;
                    xbs = si.split("=")[1].split("\\[");
                } else {//正常情况
                    nb = si;
                    // 读取下标
                    xbs = si.split("\\[");
                }
                if (xbs.length > 1) {
                    nb = xbs[0].substring(xbs[0].indexOf("'") + 1, xbs[0].lastIndexOf("'"));
                    xb = Integer.valueOf(xbs[1].substring(0, xbs[1].length() - 1));
                    if (xb <= 0) xb = 1;
                }

                List<Element> ae = new ArrayList<Element>();
                ae.addAll((List<Element>) lsE.selectNodes(nb));
                for (int i = 0; i < xb; i++) {
                    if (ae.size() < i + 1) {
                        ae.add(lsE.addElement(nb));
                    }
                }
                lsE = ae.get(xb - 1);
            }
        }

        return lsE;
    }

    /**
     * 添加节点列表到特定节点下
     */
    @SuppressWarnings("rawtypes")
    public static void addElesToEle(List es, Element parent, boolean isClearContent) {
        if (parent != null) {
            if (isClearContent) {//首次先清除所有子节点
                parent.clearContent();
            }
            if (es != null && es.size() > 0) {
                //添加所有子节点
                for (Object n : es) {
                    if (n instanceof Element) {
                        Element node = (Element) n;
                        parent.add(node.createCopy());
                    } else if (n instanceof Comment) {
                        Comment c = (Comment) n;
                        parent.addComment(c.getText());
                    }
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static void addAttrToEle(Element fromEle, Element toEle, String reg) {
        List<Attribute> attrs = fromEle.attributes();
        if (attrs != null && attrs.size() > 0) {
            for (Attribute attr : attrs) {
                String attrName = attr.getName();
                String attrValue = attr.getValue();
                if (reg != null) {
                    if (attrName.indexOf(reg) == 0) {
                        toEle.addAttribute(attrName, attrValue);
                    }
                } else {
                    toEle.addAttribute(attrName, attrValue);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static void addAttrsToEles(Element fromEle, Element toEle, String reg) {
        addAttrToEle(fromEle, toEle, reg);
        List<Element> es = fromEle.elements();
        if (es != null && es.size() > 0) {
            for (Element e : es) {
                List<Element> ts = getEle(toEle, e.getUniquePath());
                if (ts != null && ts.size() > 0) {
                    for (Element te : ts) {
                        addAttrsToEles(e, te, reg);
                    }
                }
            }
        }
    }

    /**
     * 解析XML成string，删除属性
     *
     * @throws DocumentException
     */
    public static String parseEleWithoutProp(Element e, String reg) {
        Element eCopy = (Element) e.clone();
        //遍历所有的元素节点 
        listNodes(eCopy, reg);
        return eCopy.asXML();
    }

    /**
     * 遍历当前节点元素下面的所有(元素的)子节点
     *
     * @param node
     */
    @SuppressWarnings("unchecked")
    public static void listNodes(Element node, String reg) {
        // 获取当前节点的所有属性节点  
        List<Attribute> list = node.attributes();
        // 遍历属性节点  
        for (int i = 0; i < list.size(); i++) {
            Attribute attr = list.get(i);
            //删除首字母带_的属性
            if (attr.getName().indexOf(reg) == 0) {
                node.remove(attr);
                i--;
            }
        }

        // 当前节点下面子节点迭代器  
        Iterator<Element> it = node.elementIterator();
        // 遍历  
        while (it.hasNext()) {
            // 获取某个子节点对象  
            Element e = it.next();
            // 对子节点进行遍历  
            listNodes(e, reg);
        }
    }

    /**
     * 通过特定属性替换节点
     *
     * @param eles
     * @throws DocumentException
     */
    @SuppressWarnings("unchecked")
    public static Document replaceEleByProp(Document doc, String prop, List<Element> eles) throws DocumentException {
        if (doc != null) {
            // 其实这里还是默认的目标文档
            Element root = doc.getRootElement();
            // 获取当前节点的所有属性节点
            List<Attribute> attrs = root.attributes();
            // 遍历属性节点
            for (int j = 0; j < attrs.size(); j++) {
                Attribute attr = attrs.get(j);
                //根据属性值替换element
                if (prop.equals(attr.getName())) {
                    String value = attr.getValue();
                    if ("true".equals(value)) {
                        if (eles != null && eles.size() > 0) {
                            // 这里留意一下element是node的子接口
                            Element eleRoot = eles.get(0);
                            List<Node> nodes = eleRoot.content();
                            addElesToEle(nodes, root, true);
                        }
                        listNodes(root, prop);//清除标记
                        return doc;
                    }
                }
            }
            replaceNodes(root, prop, eles);
        }
        return doc;
    }

    /**
     * 根据属性替换节点
     *
     * @throws DocumentException
     */
    @SuppressWarnings("unchecked")
    public static boolean replaceNodes(Element root, String prop, List<Element> eles) throws DocumentException {
        if (root.hasContent()) {
            // 这里的Node包括comment，text，document
            List<Node> es = root.content();
            List<Node> allEles = new LinkedList<>();
            Element p = null;
            boolean isDel = false;
            for (int i = 0; i < es.size(); i++) {
                boolean isReplace = false;
                Node node = es.get(i);
                if (node instanceof Element) {
                    Element e = (Element) node;
                    if (!isDel) {
                        // 获取当前节点的所有属性节点
                        List<Attribute> attrs = e.attributes();
                        // 遍历属性节点
                        for (int j = 0; j < attrs.size(); j++) {
                            Attribute attr = attrs.get(j);
                            // 是否有相应的属性
                            if (prop.equals(attr.getName())) {
                                String value = attr.getValue();
                                if ("true".equals(value)) {
                                    isReplace = true;
                                }
                                p = e.getParent();
                                if (eles != null && eles.size() > 0) {
                                    allEles.addAll(eles);
                                }
                                isDel = true;
                                break;
                            }
                        }
                        //递归到子节点，直到最后一个子节点
                        if (!isDel) {
                            replaceNodes(e, prop, eles);
                        }
                    }
                }
                if (!isReplace) {
                    allEles.add(node);
                }
            }
            if (isDel) {
                addElesToEle(allEles, p, true);//把p的子节点清空再把allEles放进去
                return true;
            }
        }
        return false;
    }

    /**
     * 根据xml路径获取Xml字符串
     */
    public static String getXmlStrWithXpath(String path, String xmlpath) {
        if (xmlpath != null) {
            Document doc = FileUtil.readDocFrom(path);
            if (doc != null) {
                Node node = doc.selectSingleNode(xmlpath);
                return node.asXML();
            }
        }
        return null;
    }

    /**
     * 根据xml路径获取Xml字符串
     */
    public static String getXmlStrWithXpath(String path, String xmlpath, String reg) {
        if (xmlpath != null) {
            Document doc = FileUtil.readDocFrom(path);
            if (doc != null) {
                Node node = doc.selectSingleNode(xmlpath);
                if (reg != null) {
                    if (node instanceof Element) {
                        Element ele = (Element) node;
                        //遍历所有的元素节点
                        listNodes(ele, reg);
                    }
                }
                return node.asXML();
            }
        }
        return null;
    }

    /**
     * 解析XML成string，删除属性
     *
     * @throws DocumentException
     */
    public static String getXmlStr(String path, String reg) throws DocumentException {
        Document doc = FileUtil.readDocFrom(path);
        if (reg != null) {
            Element root = doc.getRootElement();
            //遍历所有的元素节点
            listNodes(root, reg);
        }
        return doc.asXML();
    }

    /**
     * 解析XML成string，删除属性
     *
     * @throws DocumentException
     */
    public static String deleteXmlProp(String xml, String reg) throws DocumentException {
        Document doc = DocumentHelper.parseText(xml);
        if (reg != null) {
            Element root = doc.getRootElement();
            //遍历所有的元素节点
            listNodes(root, reg);
        }
        return doc.asXML();
    }


    /**
     * 根据xml路径获取Xml字符串
     *
     * @throws IOException
     */
    @SuppressWarnings("unchecked")
    public static void updateProp(String path, String newStr, String oldStr, String prop) throws IOException {
        if (oldStr != null && newStr != null) {
            if (path != null) {
                Document doc = FileUtil.readDocFrom(path);
                if (doc != null) {
                    Element root = doc.getRootElement();
                    List<Element> elements = root.elements();
                    for (Element e : elements) {
                        if (prop != null) {
                            String attrV = e.attributeValue(prop);
                            int pos = attrV.indexOf(oldStr);
                            if (pos != -1) {
                                String newAttrV = attrV.substring(0, pos) + newStr + attrV.substring(pos + oldStr.length());
                                e.addAttribute(prop, newAttrV);
                            }
                        } else {
                            List<Attribute> attrs = e.attributes();
                            for (Attribute attr : attrs) {
                                String attrV = attr.getValue();
                                int pos = attrV.indexOf(oldStr);
                                if (pos != -1) {
                                    String newAttrV = attrV.substring(0, pos) + newStr + attrV.substring(pos + oldStr.length());
                                    e.addAttribute(attr.getName(), newAttrV);
                                }
                            }
                        }
                    }
                    String xml = XmlUtil.formatXml(doc);
                    FileUtil.writeTo(xml, path);
                }
            }
        }
    }

    /**
     * map转换为xml存储
     *
     * @throws IOException
     */
    public static void mapToXml(String path, List<Map<String, Object>> maps, String rootName, String nodeName) throws IOException {
        if (maps != null && maps.size() > 0) {
            Document doc = DocumentHelper.createDocument();
            Element root = doc.addElement(rootName);
            for (Map<String, Object> map : maps) {
                Element node = root.addElement(nodeName);
                Set<Entry<String, Object>> sets = map.entrySet();
                for (Entry<String, Object> en : sets) {
                    node.addAttribute(en.getKey(), en.getValue().toString());
                }
            }

            String xml = XmlUtil.formatXml(doc);
            FileUtil.writeTo(xml, path);
        }
    }

    /**
     * xml转换为map存储
     *
     * @throws IOException
     */
    @SuppressWarnings("unchecked")
    public static List<Map<String, Object>> xmlToMap(String path) {
        if (path != null) {
            Document doc = FileUtil.readDocFrom(path);
            if (doc != null) {
                List<Map<String, Object>> maps = new ArrayList<>();

                Element root = doc.getRootElement();
                List<Element> elements = root.elements();
                for (Element e : elements) {
                    Map<String, Object> map = new HashMap<>();
                    List<Attribute> attrs = e.attributes();
                    for (Attribute attr : attrs) {
                        map.put(attr.getName(), attr.getValue());
                    }
                    maps.add(map);
                }
                return maps;
            }
        }
        return null;
    }


    @SuppressWarnings("unchecked")
    public static void replaceAttrVal(Element root, String attrName, String sourceVal, String val) {
        if (root != null) {
            String attrVal = root.attributeValue(attrName);
            if (sourceVal.equals(attrVal)) {
                root.addAttribute(attrName, val);
            }
            List<Element> cs = root.elements();
            if (cs != null && cs.size() > 0) {
                for (Element c : cs) {
                    replaceAttrVal(c, attrName, sourceVal, val);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static void replaceAttrValContain(Element root, String attrName, String containVal, String val) {
        if (root != null) {
            String attrVal = root.attributeValue(attrName);
            if (attrVal != null && attrVal.contains(containVal)) {
                root.addAttribute(attrName, val);
            }
            List<Element> cs = root.elements();
            if (cs != null && cs.size() > 0) {
                for (Element c : cs) {
                    replaceAttrValContain(c, attrName, containVal, val);
                }
            }
        }
    }

    public static void replaceAttrVal(Element root, String attrName, String val) {
        if (root != null) {
            String attrVal = root.attributeValue(attrName);
            if (attrVal != null) {
                root.addAttribute(attrName, val);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static void replaceAttrName(Element root, String oldAttrName, String newAttrName) {
        if (root != null) {
            List<Attribute> attrs = root.attributes();
            if (attrs != null && attrs.size() > 0) {
                for (Attribute attr : attrs) {
                    String attrName = attr.getName();
                    String attrVal = attr.getValue();
                    if (attrName.equals(oldAttrName)) {
                        root.remove(attr);
                        root.addAttribute(newAttrName, attrVal);
                    } else {
                        root.addAttribute(attrName, attrVal);
                    }
                }
            }

            List<Element> es = root.elements();
            if (es != null && es.size() > 0) {
                for (Element e : es) {
                    replaceAttrName(e, oldAttrName, newAttrName);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static void delAttr(Element root, String nodeName, String attrName) {
        if (root != null && nodeName != null) {
            List<Element> es = root.elements();
            if (es != null && es.size() > 0) {
                for (Element e : es) {
                    String eName = e.getName();
                    if (eName.equals(nodeName)) {
                        List<Attribute> attrs = e.attributes();
                        if (attrs != null && attrs.size() > 0) {
                            for (Attribute attr : attrs) {
                                if (attrName.equals(attr.getName())) {
                                    e.remove(attr);
                                    break;
                                }
                            }
                        }
                    }

                    List<Element> cs = e.elements();
                    if (cs != null && cs.size() > 0) {
                        for (Element c : cs) {
                            delAttr(c, nodeName, attrName);
                        }
                    }
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static void delAttrVal(Element root, String attrName) {
        if (root != null) {
            String attrVal = root.attributeValue(attrName);
            if (attrVal != null && !"".equals(attrVal)) {
                root.addAttribute(attrName, "");
            }
            List<Element> cs = root.elements();
            if (cs != null && cs.size() > 0) {
                for (Element c : cs) {
                    delAttrVal(c, attrName);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static void delElementByAttr(Element parentElement, String attrName, String attrVal) {
        if (parentElement != null) {
            List<Element> es = parentElement.elements();
            if (es != null && !es.isEmpty()) {
                for (Element e : es) {
                    String val = e.attributeValue(attrName);
                    if (val != null && val.equals(attrVal)) {
                        parentElement.remove(e);
                    } else {
                        // 递归删除有"_del=true"的element
                        delElementByAttr(e, attrName, attrVal);
                    }
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static Element getElementByName(Element root, String name, String exclueEleName) {
        if (root != null && name != null) {
            if (exclueEleName != null) {
                Element parent = root.getParent();
                if (root.getName().equals(name) && (parent != null && !parent.getName().equals(exclueEleName))) {
                    return root;
                } else {
                    List<Element> es = root.elements();
                    if (es != null && es.size() > 0) {
                        for (Element e : es) {
                            Element ce = getElementByName(e, name, exclueEleName);
                            if (ce != null) {
                                return ce;
                            }
                        }
                    }
                }
            } else {
                if (root.getName().equals(name)) {
                    return root;
                } else {
                    List<Element> es = root.elements();
                    if (es != null && es.size() > 0) {
                        for (Element e : es) {
                            Element ce = getElementByName(e, name, null);
                            if (ce != null) {
                                return ce;
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public static void copyVal(Element fromEle, Element toEle) {
        if (toEle != null && fromEle != null) {
            toEle.setText(fromEle.getText());//文本
            List<Attribute> attrs = toEle.attributes();
            if (attrs != null && attrs.size() > 0) {//属性
                for (Attribute attr : attrs) {
                    String attrName = attr.getName();
                    String fromVal = fromEle.attributeValue(attr.getName());
                    if ("extension".equals(attrName) || "code".equals(attrName) || "value".equals(attrName) || "displayName".equals(attrName)) {
                        toEle.addAttribute(attrName, fromVal);
                    }
                }
            }

            List<Element> tes = toEle.elements();
            if (tes != null && tes.size() > 0) {
                for (Element te : tes) {
                    String tUniquePath = te.getUniquePath();
                    int lastIndexOf = tUniquePath.lastIndexOf("/");
                    String te_lastPath = tUniquePath.substring(lastIndexOf);
                    String fPath = fromEle.getUniquePath() + te_lastPath;
                    List<Element> es = getEles(fromEle, fPath);
                    if (es != null && es.size() > 0) {
                        for (Element fe : es) {
                            copyVal(fe, te);
                            break;
                        }
                    }
                }
            }
        }
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    public static Map<String, Object> elementToMap(Element e) {
        if (e != null) {
            Map<String, Object> rm = new HashMap<>();
            List<Element> childs = e.elements();
            if (childs != null && !childs.isEmpty()) {
                Map<String, Object> map = new HashMap<>();
                for (Element child : childs) {
                    Map<String, Object> childMap = elementToMap(child);
                    if (childMap != null) {
                        map.putAll(childMap);
                    }
                }

                //考虑循环节点
                if (rm.containsKey(e.getName())) {
                    Object obj = rm.get(e.getName());
                    if (obj instanceof List) {
                        ((List) obj).add(map);
                    } else {
                        List list = new ArrayList<>();
                        list.add(obj);
                        list.add(map);
                        rm.put(e.getName(), list);
                    }
                } else {
                    rm.put(e.getName(), map);
                }
            } else {
                //考虑循环节点
                if (rm.containsKey(e.getName())) {
                    Object obj = rm.get(e.getName());
                    if (obj instanceof List) {
                        ((List) obj).add(e.getTextTrim());
                    } else {
                        List list = new ArrayList<>();
                        list.add(obj);
                        list.add(e.getTextTrim());
                        rm.put(e.getName(), list);
                    }
                } else {
                    rm.put(e.getName(), e.getTextTrim());
                }
            }
            return rm;
        }
        return null;
    }

}
